package miao_ser

import (
	"encoding/json"
	"gitee.com/ling1314/origin-utility/db"
	"gitee.com/ling1314/origin-utility/model"
	"gitee.com/ling1314/origin-utility/modules/repository"
	"gitee.com/ling1314/origin-utility/utils"
	log "github.com/sirupsen/logrus"
	"strings"
	"time"
)

type viHandler struct {
}

func NewViHandler() *viHandler {
	return &viHandler{}
}

// 处理举手消息
func (s *viHandler) getDeviceId(msg model.HandsUpMsgForVi) (deviceID int64, err error) {
	var (
		li []model.MiaoClient
	)
	// 1. 查询数据库，确认设备是否已经被收录，如果没有就插入，如果有就直接返回设备ID
	li, err = repository.MiaoTerminalRepository.FindProbableList(msg.BiosSerial, msg.MainBoardSerial, msg.HardDiskSerial, msg.ActiveMAC, msg.Hostname)
	if err != nil {
		log.Errorf("[ err: %v] = [HandsUpMsg :%v]", err, msg)
		return
	}
	if len(li) == 0 {
		item := model.MiaoClient{
			DeviceID:        utils.GetSoftId(),
			BiosSerial:      msg.BiosSerial,
			MainBoardSerial: msg.MainBoardSerial,
			HardDiskSerial:  msg.HardDiskSerial,
			ActiveMAC:       msg.ActiveMAC,
			IP:              msg.IP,
			Hostname:        msg.Hostname,
			CreatedAt:       time.Now().Unix(),
			IsOverlap:       false,
		}
		err = repository.MiaoTerminalRepository.Save(item)
		if err != nil {
			log.Errorf("[err :%v] = [argus:%v]", err, item)
			return
		}
		return item.DeviceID, nil
	}
	// 如果已经存在，就需要检查是否存在重复
	if len(li) == 1 {
		return li[0].DeviceID, nil
	}
	uli := make([]model.MiaoClient, 0)
	for _, item := range li {
		score := 0
		if msg.BiosSerial == item.BiosSerial {
			score++
		}
		if msg.MainBoardSerial == item.MainBoardSerial {
			score++
		}
		if msg.HardDiskSerial == item.HardDiskSerial {
			score++
		}
		if msg.Hostname == item.Hostname {
			score++
		}
		if msg.ActiveMAC == item.ActiveMAC {
			score++
		}
		//如果5项中有3项一样，就判定为原来的设备发生了更新，例如更换零件或者重装系统
		if score >= 3 {
			uli = append(uli, item)
		}
	}
	if len(uli) == 1 {
		return uli[0].DeviceID, nil
	}
	// 长度超过1，说明可能存在采集信息相同的设备，需要管理员人为干预
	for _, item := range uli {
		if !item.IsOverlap {
			item.IsOverlap = true
			err = repository.MiaoTerminalRepository.Save(item)
			if err != nil {
				log.Errorf("[err: %v] = [value:%v]", err, item)
			}
		}
	}
	// todo 这里应该设置一个错误任务队列，将意外情况汇报给管理员。
	return -1, nil
}

// HandleGuarderPipMsg  处理守卫进程管道中读取到的消息
func (s *viHandler) HandleGuarderPipMsg(m model.ClientResponse) {
	var (
		bi  model.ResultMsg
		err error
		id  int64
	)
	err = json.Unmarshal([]byte(m.Content.Result), &bi)
	if err != nil {
		log.Errorf("不合法的 guarder pip 消息：%v", m)
		return
	}
	pc := db.InfoCatchMaoForIdp[m.DeviceId]
	switch bi.MsgType {
	case "cpu":
		var c model.CpuMsg
		if err = json.Unmarshal([]byte(bi.Result), &c); err != nil {
			log.Error(err)
			return
		}
		pc.LastCpuWarn = c
		if err = repository.IdpWarnRepository.SaveLoadWarn(model.IdpWarnLoad{
			Id:        utils.GetSoftId(),
			DeviceId:  id,
			LoadType:  "cpu",
			CreatedAt: time.Now().Unix(),
			Content:   bi.Result,
			Sample:    c.Sample,
		}); err != nil {
			log.Error(err)
			return
		}
	case "memory":
		var c model.MemoryMsg
		if err = json.Unmarshal([]byte(bi.Result), &c); err != nil {
			log.Error(err)
			return
		}
		pc.LastMemoryWarn = c
		if err = repository.IdpWarnRepository.SaveLoadWarn(model.IdpWarnLoad{
			Id:        utils.GetSoftId(),
			DeviceId:  id,
			LoadType:  "memory",
			CreatedAt: time.Now().Unix(),
			Content:   bi.Result,
			Sample:    c.Sample,
		}); err != nil {
			log.Error(err)
			return
		}
	case "session":
		var c model.WinSessionChangeMsg
		if err = json.Unmarshal([]byte(bi.Result), &c); err != nil {
			log.Error(err)
			return
		}
		pc.LastWinSession = c
		if err = repository.IdpWarnRepository.SaveHostnameChange(model.IdpWarnHostname{
			Id:        utils.GetSoftId(),
			DeviceId:  id,
			CreatedAt: time.Now().Unix(),
			Content:   bi.Result,
			Hostname:  c.Username,
			Describe:  c.Describe,
		}); err != nil {
			log.Error(err)
			return
		}
	default:
		log.Error("unknown guarder msg:", m)
	}
	db.InfoCatchMaoForIdp[m.DeviceId] = pc
}

// HandleCliMsg 处理插件消息
func (s *viHandler) HandleCliMsg(m model.ClientResponse) {
	switch m.Content.Plugin {
	case "cpu":
		s.cpu(m.DeviceId, m.Content)
	case "memory":
		s.memory(m.DeviceId, m.Content)
	case "disk":
		s.disk(m.DeviceId, m.Content)
	case "board":
		s.board(m.DeviceId, m.Content)
	case "sys_abstract":
		s.sysAbstract(m.DeviceId, m.Content)
	case "app_list":
		s.app(m.DeviceId, m.Content)
	default:
		log.Error("未知插件")
	}
}

func (s *viHandler) cpu(deviceId int64, pluginResult model.PluginResult) {
	args := ""
	for i := range pluginResult.Args {
		args += pluginResult.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "":
		var r model.RCpu
		_ = json.Unmarshal([]byte(pluginResult.Result), &r)
		if len(r) > 0 {
			cache := db.InfoCatchMaoForIdp[deviceId]
			if cache.CPU.Name != r[0].Name || cache.CPU.ProcessorId != r[0].ProcessorID {
				l := model.IdpLogCpu{
					IdpLogBase: model.IdpLogBase{
						Id:        utils.GetSoftId(),
						DeviceId:  deviceId,
						CreatedAt: time.Now().Unix(),
						Content:   pluginResult.Result,
					},
					Name:        r[0].Name,
					ProcessorId: r[0].ProcessorID,
				}
				cache.CPU = l
				db.InfoCatchMaoForIdp[deviceId] = cache
				if err := repository.IdpLogRepository.SaveLogCpu(&l); err != nil {
					log.Error(err)
					return
				}
			}
		}
	case "-id":
		log.Debug(pluginResult.Result)
	default:
		log.Error("未知的cpu 参数")
	}
}

func (s *viHandler) memory(deviceId int64, pluginResult model.PluginResult) {
	args := ""
	for i := range pluginResult.Args {
		args += pluginResult.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "":
		var r model.RMemory
		_ = json.Unmarshal([]byte(pluginResult.Result), &r)
		var (
			details   []string
			totalSize int64
		)
		for i := range r {
			details = append(details, r[i].SerialNumber)
			totalSize += r[i].Capacity
		}
		bs, _ := json.Marshal(details)
		cache := db.InfoCatchMaoForIdp[deviceId]
		if cache.Memory.Details != string(bs) ||
			cache.Memory.Num != len(r) ||
			cache.Memory.TotalSize != totalSize {
			l := model.IdpLogMemory{
				IdpLogBase: model.IdpLogBase{
					Id:        utils.GetSoftId(),
					DeviceId:  deviceId,
					CreatedAt: time.Now().Unix(),
					Content:   pluginResult.Result,
				},
				TotalSize: totalSize,
				Num:       len(r),
				Details:   string(bs),
			}
			cache.Memory = l
			db.InfoCatchMaoForIdp[deviceId] = cache
			if err := repository.IdpLogRepository.SaveLogMemory(&l); err != nil {
				log.Error(err)
				return
			}

		}
	case "-serial":
		log.Debug(pluginResult.Result)
	default:
		log.Debug("未知的memory参数")
	}
}

func (s *viHandler) disk(deviceId int64, pr model.PluginResult) {
	args := ""
	for i := range pr.Args {
		args += pr.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "-type=physics":
		var r model.RPhysicsDisk
		_ = json.Unmarshal([]byte(pr.Result), &r)
		var (
			details   []string
			totalSize int64
		)
		for i := range r {
			details = append(details, r[i].SerialNumber)
			totalSize += r[i].Size
		}
		bs, _ := json.Marshal(details)
		cache := db.InfoCatchMaoForIdp[deviceId]
		if cache.DiskPhysics.Details != string(bs) ||
			cache.DiskPhysics.Num != len(r) ||
			cache.DiskPhysics.TotalSize != totalSize {
			l := model.IdpLogDisk{
				IdpLogBase: model.IdpLogBase{
					Id:        utils.GetSoftId(),
					DeviceId:  deviceId,
					CreatedAt: time.Now().Unix(),
					Content:   pr.Result,
				},
				TotalSize: totalSize,
				Num:       len(r),
				Details:   string(bs),
			}
			cache.DiskPhysics = l
			db.InfoCatchMaoForIdp[deviceId] = cache
			if err := repository.IdpLogRepository.SaveLogDisk(&l); err != nil {
				log.Error(err)
				return
			}
		}
	case "-type=logic":
		var r model.RLogicDisk
		_ = json.Unmarshal([]byte(pr.Result), &r)
		cache := db.InfoCatchMaoForIdp[deviceId]
		cache.DiskLogic = r
		db.InfoCatchMaoForIdp[deviceId] = cache
	case "-type=physics -s":
		log.Debug(pr.Result)
	case "-type=logic -s":
		log.Debug(pr.Result)
	default:
		log.Debug("未知的disk参数")
	}

}

func (s *viHandler) board(deviceId int64, pr model.PluginResult) {
	args := ""
	for i := range pr.Args {
		args += pr.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "-type=base":
		var r model.RBoardBase
		_ = json.Unmarshal([]byte(pr.Result), &r)
		cache := db.InfoCatchMaoForIdp[deviceId]
		if cache.BoardBase.Serial != r.BoardSerialNumber {
			l := model.IdpLogBoard{
				IdpLogBase: model.IdpLogBase{
					Id:        utils.GetSoftId(),
					DeviceId:  deviceId,
					CreatedAt: time.Now().Unix(),
					Content:   pr.Result,
				},
				Serial: r.BoardSerialNumber,
			}
			cache.BoardBase = l
			db.InfoCatchMaoForIdp[deviceId] = cache
			if err := repository.IdpLogRepository.SaveLogBoard(&l); err != nil {
				log.Error(err)
				return
			}
		}
	case "-type=bios":
		var r model.RBios
		_ = json.Unmarshal([]byte(pr.Result), &r)
		cache := db.InfoCatchMaoForIdp[deviceId]
		cache.BoardBios = r
		db.InfoCatchMaoForIdp[deviceId] = cache
	case "-type=board":
		var r model.RMainBoard
		_ = json.Unmarshal([]byte(pr.Result), &r)
		cache := db.InfoCatchMaoForIdp[deviceId]
		cache.BoardMain = r
		db.InfoCatchMaoForIdp[deviceId] = cache
	case "-type=board -s":
		log.Debug(pr)
	case "-type=bios -s":
		log.Debug(pr)
	case "-type=base -s":
		log.Debug(pr)
	default:
		log.Error("未知的board参数")
	}
}

func (s *viHandler) sysAbstract(deviceId int64, pr model.PluginResult) {
	args := ""
	for i := range pr.Args {
		args += pr.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "":
		var r model.RSysAbstract
		_ = json.Unmarshal([]byte(pr.Result), &r)
		cache := db.InfoCatchMaoForIdp[deviceId]
		if cache.SysAbstract.Caption != r.Caption ||
			cache.SysAbstract.Version != r.Version ||
			cache.SysAbstract.SerialNumber != r.SerialNumber {
			l := model.IdpLogSysAbstract{
				IdpLogBase: model.IdpLogBase{
					Id:        utils.GetSoftId(),
					DeviceId:  deviceId,
					CreatedAt: time.Now().Unix(),
					Content:   pr.Result,
				},
				Caption:      r.Caption,
				Version:      r.Version,
				SerialNumber: r.SerialNumber,
			}
			cache.SysAbstract = l
			db.InfoCatchMaoForIdp[deviceId] = cache
			if err := repository.IdpLogRepository.SaveLogSysAbstract(&l); err != nil {
				log.Error(err)
				return
			}
		}
	default:
		log.Debug("未知的sys_abstract参数")

	}
}

func (s *viHandler) app(deviceId int64, pr model.PluginResult) {
	args := ""
	for i := range pr.Args {
		args += pr.Args[i]
		args += " "
	}
	switch strings.TrimSpace(args) {
	case "":
		fallthrough
	case "-appList":
		oldLog, err := repository.IdpLogRepository.FindLastApp(deviceId)
		if err != nil {
			log.Error(err)
			return
		}
		// 采集到的数据和数据库保存的数据不一致时需要写入新记录
		if oldLog.Content != pr.Result {
			l := model.IdpLogApp{
				IdpLogBase: model.IdpLogBase{
					Id:        utils.GetSoftId(),
					DeviceId:  deviceId,
					CreatedAt: time.Now().Unix(),
					Content:   pr.Result,
				},
			}
			if err = repository.IdpLogRepository.SaveLogApp(&l); err != nil {
				log.Error(err)
			}
		}
		// 检查变化的部分，写入 app_change_log
		s.appChangelog(deviceId, pr, oldLog)
	}
}

func (s *viHandler) appChangelog(deviceId int64, pr model.PluginResult, oldLog model.IdpLogApp) {
	var (
		changes []model.IdpAppChangeLog
		newLi   model.RApps
		oldLi   model.RApps
	)
	_ = json.Unmarshal([]byte(pr.Result), &newLi)
	_ = json.Unmarshal([]byte(oldLog.Content), &oldLi)
	if len(newLi) > 0 && len(oldLi) == 0 {
		for _, v := range newLi {
			changes = append(changes, model.IdpAppChangeLog{
				Id:              utils.GetSoftId(),
				DeviceId:        deviceId,
				CreatedAt:       time.Now().Unix(),
				DisplayName:     v.DisplayName,
				DisplayVersion:  v.DisplayVersion,
				Publisher:       v.Publisher,
				InstallDate:     v.InstallDate,
				InstallLocation: v.InstallLocation,
				Change:          "初始记录",
			})
		}
	} else {
		for _, v := range newLi {
			if !utils.Contains(oldLi, v) {
				changes = append(changes, model.IdpAppChangeLog{
					Id:              utils.GetSoftId(),
					DeviceId:        deviceId,
					CreatedAt:       time.Now().Unix(),
					DisplayName:     v.DisplayName,
					DisplayVersion:  v.DisplayVersion,
					Publisher:       v.Publisher,
					InstallDate:     v.InstallDate,
					InstallLocation: v.InstallLocation,
					Change:          "新增",
				})
			}
		}
		for _, v := range oldLi {
			if !utils.Contains(newLi, v) {
				changes = append(changes, model.IdpAppChangeLog{
					Id:              utils.GetSoftId(),
					DeviceId:        deviceId,
					CreatedAt:       time.Now().Unix(),
					DisplayName:     v.DisplayName,
					DisplayVersion:  v.DisplayVersion,
					Publisher:       v.Publisher,
					InstallDate:     v.InstallDate,
					InstallLocation: v.InstallLocation,
					Change:          "移除",
				})
			}
		}
	}
	if len(changes) > 0 {
		if err := repository.IdpLogRepository.SaveAppChangeLog(changes); err != nil {
			log.Error(err)
			return
		}
	}

}
